{{define "theme-default/network"}}
{{template "theme-default/header" .}}
{{if ts .CustomCode}}
{{.CustomCode|safe}}
{{end}}
{{template "theme-default/menu" .}}
<div class="nb-container">
    <div class="ui container">
        <div class="service-status">

            <div class="server-buttons-container">
                <button class="ui network-primary-btn icon button" v-for="server in servers" :key="server.ID"
                    style="margin-top: 3px;font-size: 100%;" @click="redirectNetwork(server.ID)">
                    <i :class="'fi fi-'+server.Host.CountryCode"></i>&nbsp;<i v-if='server.Host.Platform == "darwin"'
                        class="apple icon"></i><i v-else-if='isWindowsPlatform(server.Host.Platform)'
                        class="windows icon"></i><i v-else :class="'fl-' + getFontLogoClass(server.Host.Platform)"></i>
                    @#server.Name#@
                </button>
            </div>
        </div>
    </div>
    <div class="ui container">
        <div ref="chartDom" style="margin-top: 10px;height: auto;overflow: hidden;border-radius: 10px;"></div>
    </div>
</div>

{{template "common/footer" .}}

<script>
    // 性能优化措施
    if ('requestIdleCallback' in window) {
        // 使用空闲时间预处理数据
        requestIdleCallback(() => {
            // 预加载字体
            if (document.fonts && document.fonts.load) {
                document.fonts.load('1em "Font Awesome 5 Free"').catch(() => { });
                document.fonts.load('1em "Font Awesome 5 Brands"').catch(() => { });
                document.fonts.load('1em "Font Awesome 6 Pro"').catch(() => { });
                document.fonts.load('1em "Font Awesome 6 Brands"').catch(() => { });
                document.fonts.load('1em "Font Awesome 6 Free"').catch(() => { });
            }
        });
    }

    // 减少重排提示
    const originalSetTimeout = window.setTimeout;
    let timeoutCount = 0;
    window.setTimeout = function (fn, delay) {
        timeoutCount++;
        if (timeoutCount > 10) {
            // 批量处理过多的setTimeout
            return originalSetTimeout(() => {
                requestAnimationFrame(fn);
            }, delay || 0);
        }
        return originalSetTimeout(fn, delay);
    };

    const monitorInfo = JSON.parse('{{.MonitorInfos}}');
    const initData = JSON.parse('{{.Servers}}').servers;
    const defaultServerID = {{.DefaultServerID}} || (initData && initData[0] ? initData[0].ID : null);
    let MaxTCPPingValue = {{.MaxTCPPingValue }};

    // 性能优化：移除同步AJAX，改为异步加载
    let monitorConfigs = {};

    // 异步加载监控配置，不阻塞页面渲染
    function loadMonitorConfigs() {
        return $.ajax({
            url: '/api/v1/monitor/configs',
            method: 'GET',
            timeout: 5000,
            cache: true
        }).done(function (data) {
            if (data && Array.isArray(data)) {
                data.forEach(monitor => {
                    monitorConfigs[monitor.ID] = monitor;
                });
            }
        }).fail(function () {
            // 无法获取监控配置，使用默认名称
        });
    }
    // 确保DOM加载完成后再初始化Vue
    document.addEventListener('DOMContentLoaded', function () {
        new Vue({
            el: '#app',
            delimiters: ['@#', '#@'],
            data: {
                page: 'network',
                defaultTemplate: {{.Conf.Site.Theme }},
        templates: {{.Themes }},
        servers: initData,
        option: {},
    resizeTimer: null,
    tooltipTimer: null,
        monitorConfigsLoaded: false,
        loadedFromAPI: false,
        timeRange: '24h', // 默认显示24小时
    currentServerId: null, // 当前选中的服务器ID
    hoverTipLabel: '' // 悬停在自定义按钮时的临时 tooltip 文本
        },
        mixins: [mixinsVue],
        created() {
    const vm = this;
    this.option = {
            tooltip: {
                trigger: 'axis',
                position: function (pt) {
                    return [pt[0], '10%'];
                },
                // 使用 HTML 渲染以显示彩色 marker
                renderMode: 'html',
                formatter: function (params) {
                    if (vm && vm.hoverTipLabel) {
                        return vm.hoverTipLabel;
                    }
                    if (!params) return '';

                    // 非数组：可能是 graphic 按钮等
                    if (!Array.isArray(params)) {
                        var p = params;
                        if (p && p.componentType === 'graphic') {
                            var key = (p.id || p.name || '').toString();
                            if (key.indexOf('btn24h') !== -1) return '24 小时';
                            if (key.indexOf('btn72h') !== -1) return '72 小时';
                        }
                        return '';
                    }

                    if (params.length === 0) return '';

                    // 构造 HTML 字符串，首行时间，随后每行：彩色 marker + 名称 + 数值
                    const lines = [];
                    lines.push(params[0].axisValueLabel);

                    // 按监控点名称排序，确保显示顺序一致（拷贝一份避免原数组被修改）
                    const sortedParams = params.slice().sort((a, b) => {
                        return a.seriesName.localeCompare(b.seriesName);
                    });

                    sortedParams.forEach(function (item) {
                        if (item.value && item.value[1] !== undefined) {
                            const delay = parseFloat(item.value[1]);
                            const displayDelay = isNaN(delay) ? '超时' : delay.toFixed(2) + ' ms';
                            lines.push(item.marker + item.seriesName + ": " + displayDelay);
                        }
                    });

                    return lines.join('<br/>');
                },
                padding: [8, 10],
                textStyle: { fontSize: 12, lineHeight: 16 },
                appendToBody: false,
                confine: true,
                transitionDuration: 0,
                z: 10,
                zlevel: 1,
                // 优化：确保显示所有数据系列
                showContent: true,
                triggerOn: 'mousemove',
                axisPointer: {
                    type: 'cross',
                    crossStyle: {
                        color: '#999'
                    }
                }
            },
            title: {
                show: true,
                left: 'center',
                text: "",
                textStyle: {},
                top: 8,
                z: 5,
                zlevel: 0
            },
            legend: {
                right: this.isMobile ? '150px' : '160px', // 为按钮留出空间，避免重叠，移动端整体左移
                data: [],
                top: 44,
                textStyle: {
                    fontSize: this.isMobile ? 11 : 14 // 移动端使用更小字体
                },
                itemWidth: this.isMobile ? 15 : 25, // 移动端缩小图例图标
                itemHeight: this.isMobile ? 10 : 14,
                itemGap: this.isMobile ? 8 : 10, // 移动端减少间距
                z: 4,
                zlevel: 0
            },
            grid: {
                left: this.isMobile ? '12%' : '7%', // 进一步增加左边距
                right: this.isMobile ? '12%' : '7%', // 进一步增加右边距
                bottom: this.isMobile ? '13%' : '12%', // PC端略微增加，移动端减少
                z: 1,
                zlevel: 0
            },
            backgroundColor: 'rgba(255, 255, 255, 0.6)',
            dataZoom: [
                {
                    type: 'slider',
                    start: 0,
                    end: 100,
                    bottom: this.isMobile ? '2%' : '1%', // 调整滑动条位置
                    height: this.isMobile ? 20 : 28, // PC端略微再增加高度
                    z: 6,
                    zlevel: 0,
                    labelFormatter: function (value) {
                        // 滑动条两端时间字符串换行显示
                        return echarts.format.formatTime('MM-dd\nhh:mm', value);
                    }
                }
            ],
            xAxis: {
                type: 'time',
                boundaryGap: false,
                axisLabel: {
                    formatter: function (value) {
                        // PC端不换行，移动端换行
                        if (window.innerWidth <= 768) {
                            return echarts.format.formatTime('MM-dd\nhh:mm', value);
                        }
                        return echarts.format.formatTime('MM-dd hh:mm', value);
                    },
                    interval: 'auto', // 自动计算间隔
                    rotate: 0, // 不旋转
                    fontSize: window.innerWidth <= 768 ? 10 : 12, // 移动端使用更小字体
                    margin: window.innerWidth <= 768 ? 8 : 8, // 增加边距
                    showMaxLabel: true,
                    showMinLabel: true,
                    lineHeight: window.innerWidth <= 768 ? 16 : 14 // 移动端需要更大行高
                },
                z: 1,
                zlevel: 0
            },
            yAxis: {
                type: 'value',
                boundaryGap: false,
                z: 1,
                zlevel: 0
            },
            series: [],
            animation: false,
            animationDuration: 0,
            graphic: [
                // 24小时按钮（分组）
                {
                    type: 'group',
                    id: 'btn24hGroup',
                    right: 75,
                    top: 15,
            silent: false,
            tooltip: { show: false },
                    cursor: 'pointer',
                    onclick: function(){ try{ vm.switchTimeRange('24h'); }catch(e){} },
                    children: [
                        {
                            type: 'rect',
                            id: 'btn24hRect',
                silent: false,
                tooltip: { show: false },
                            onclick: function(){ try{ vm.switchTimeRange('24h'); }catch(e){} },
                            shape: { width: 60, height: 24, r: [4, 0, 0, 4] },
                            style: {
                                fill: this.timeRange === '24h' ? 'rgba(255,255,255,0.6)' : 'rgba(255,255,255,0.4)',
                                stroke: 'transparent', lineWidth: 0,
                                shadowColor: 'rgba(0,0,0,0.08)', shadowBlur: 2, shadowOffsetY: 1
                            },
                            z: 100
                        },
                        {
                            type: 'text',
                            id: 'btn24hText',
                            tooltip: { show: false },
                            silent: true,
                            style: {
                                text: '24 小时',
                                fontSize: this.isMobile ? 11 : 12,
                                fontWeight: this.timeRange === '24h' ? 'bold' : 'normal',
                                fill: this.timeRange === '24h' ? '#333' : '#666',
                                x: 30, y: 12,
                                align: 'center', verticalAlign: 'middle'
                            },
                            z: 101
                        }
                    ]
                },
                // 72小时按钮（分组）
                {
                    type: 'group',
                    id: 'btn72hGroup',
                    right: 15,
                    top: 15,
            silent: false,
            tooltip: { show: false },
                    cursor: 'pointer',
                    onclick: function(){ try{ vm.switchTimeRange('72h'); }catch(e){} },
                    children: [
                        {
                            type: 'rect',
                            id: 'btn72hRect',
                silent: false,
                tooltip: { show: false },
                            onclick: function(){ try{ vm.switchTimeRange('72h'); }catch(e){} },
                            shape: { width: 60, height: 24, r: [0, 4, 4, 0] },
                            style: {
                                fill: this.timeRange === '72h' ? 'rgba(255,255,255,0.6)' : 'rgba(255,255,255,0.4)',
                                stroke: 'transparent', lineWidth: 0,
                                shadowColor: 'rgba(0,0,0,0.08)', shadowBlur: 2, shadowOffsetY: 1
                            },
                            z: 100
                        },
                        {
                            type: 'text',
                            id: 'btn72hText',
                            tooltip: { show: false },
                            silent: true,
                            style: {
                                text: '72 小时',
                                fontSize: this.isMobile ? 11 : 12,
                                fontWeight: this.timeRange === '72h' ? 'bold' : 'normal',
                                fill: this.timeRange === '72h' ? '#333' : '#666',
                                x: 30, y: 12,
                                align: 'center', verticalAlign: 'middle'
                            },
                            z: 101
                        }
                    ]
                }
            ]
        };
    },
        mounted() {
        // 使用passive事件监听器优化滚轮性能
        window.addEventListener('resize', this.resizeHandle, { passive: true });

        // 温和的移动端滚动优化
        if(this.isMobile) {
        // 简单的滚动优化，不强制改变页面结构
        document.body.style.webkitOverflowScrolling = 'touch';

        // 防止意外的页面跳转
        let lastScrollTop = 0;
        window.addEventListener('scroll', function () {
            const currentScrollTop = window.pageYOffset || document.documentElement.scrollTop;
            lastScrollTop = currentScrollTop;
        }, { passive: true });
    }

    // 优化初始化序列，先加载监控配置
    this.$nextTick(() => {
    // 先渲染图表
    this.renderChart();

        // 先加载监控配置，然后再处理数据
        loadMonitorConfigs().always(() => {
            this.monitorConfigsLoaded = true;

            // 将当前选中服务器默认设置为列表中的第一个，确保后续切换时间范围触发 API 拉取
            if (!this.currentServerId) {
                this.currentServerId = defaultServerID;
            }

            // 然后加载数据，避免阻塞UI，默认显示24小时数据
            setTimeout(() => {
                // 首屏先用预取数据渲染
                this.filterInitialDataByTimeRange(this.timeRange);
                // 如预取窗口不足，再异步用后端完整窗口刷新一次
                if (this.currentServerId && !this.hasFullWindowForCurrent(this.timeRange)) {
                    this.getMonitorHistory(this.currentServerId, this.timeRange)
                        .then((monitorInfo) => { this.parseMonitorInfo(monitorInfo); })
                        .catch(() => { /* 保留预取数据 */ });
                }
            }, 50);
        });
    });
        },
    destroyed() {
        window.removeEventListener('resize', this.resizeHandle, { passive: true });
    },
    methods: {
        // 判断当前 server 的首屏预取数据是否覆盖了完整时间窗口
        hasFullWindowForCurrent(range) {
            try {
                if (!this.currentServerId || !Array.isArray(monitorInfo) || monitorInfo.length === 0) return false;
                const timeLimit = range === '24h' ? 24 * 60 * 60 * 1000 : 72 * 60 * 60 * 1000;
                const cutoff = Date.now() - timeLimit + 60000; // 放宽1分钟
                let minTs = Infinity;
                for (let i = 0; i < monitorInfo.length; i++) {
                    const it = monitorInfo[i];
                    const sid = it.ServerID || it.server_id;
                    if (sid !== this.currentServerId) continue;
                    const ts = Date.parse(it.CreatedAt || it.created_at);
                    if (!isNaN(ts) && ts < minTs) minTs = ts;
                }
                return minTs !== Infinity && minTs <= cutoff;
            } catch (e) { return false; }
        },
        // 极致性能优化：简化数据处理逻辑
        processMonitorHistories(histories) {
            if (!histories || histories.length === 0) {
                return [];
            }

            // 性能优化：使用普通对象而非Map，减少开销
            const groupedData = {};
            const serverNames = {};

            // 预构建服务器名称映射
            if (this.servers) {
                for (let i = 0; i < this.servers.length; i++) {
                    const server = this.servers[i];
                    serverNames[server.ID] = server.Name;
                }
            }

            // 极致优化：单次遍历完成所有处理
            for (let i = 0; i < histories.length; i++) {
                const history = histories[i];
                const monitorId = history.MonitorID || history.monitor_id || 0;
                const serverId = history.ServerID || history.server_id || 0;
                const uniqueKey = `${monitorId}_${serverId}`;

                if (!groupedData[uniqueKey]) {
                    groupedData[uniqueKey] = {
                        created_at: [],
                        avg_delay: [],
                        monitor_id: monitorId,
                        server_id: serverId,
                        unique_key: uniqueKey
                    };
                }

                const data = groupedData[uniqueKey];

                // 性能优化：减少日期转换，直接使用字符串解析
                let timestamp;
                const timeStr = history.CreatedAt || history.created_at;
                if (timeStr) {
                    timestamp = Date.parse(timeStr);
                } else {
                    timestamp = Date.now();
                }

                const avgDelay = history.AvgDelay || history.avg_delay || 0;

                data.created_at.push(timestamp);
                data.avg_delay.push(avgDelay);
            }

            // 性能优化：转换为图表需要的格式，减少数组操作
            const result = [];

            for (const uniqueKey in groupedData) {
                const data = groupedData[uniqueKey];
                const monitorId = data.monitor_id;
                const serverId = data.server_id;

                // 性能优化：只在数据量大时才排序，小数据量保持原序
                if (data.created_at.length > 100) {
                    // 将时间戳和延迟数据配对并排序
                    const paired = [];
                    for (let i = 0; i < data.created_at.length; i++) {
                        paired.push({
                            time: data.created_at[i],
                            delay: data.avg_delay[i]
                        });
                    }
                    paired.sort((a, b) => a.time - b.time);

                    result.push({
                        monitor_id: monitorId,
                        server_id: serverId,
                        unique_key: uniqueKey,
                        monitor_name: this.getMonitorName(monitorId),
                        server_name: serverNames[serverId] || `Server ${serverId}`,
                        created_at: paired.map(p => p.time),
                        avg_delay: paired.map(p => p.delay)
                    });
                } else {
                    // 小数据量直接使用，避免排序开销
                    result.push({
                        monitor_id: monitorId,
                        server_id: serverId,
                        unique_key: uniqueKey,
                        monitor_name: this.getMonitorName(monitorId),
                        server_name: serverNames[serverId] || `Server ${serverId}`,
                        created_at: data.created_at,
                        avg_delay: data.avg_delay
                    });
                }
            }


            return result;
        },
        getFontLogoClass(str) {
            if (["almalinux",
                "alpine",
                "aosc",
                "apple",
                "archlinux",
                "archlabs",
                "artix",
                "budgie",
                "centos",
                "coreos",
                "debian",
                "deepin",
                "devuan",
                "docker",
                "elementary",
                "fedora",
                "ferris",
                "flathub",
                "freebsd",
                "gentoo",
                "gnu-guix",
                "illumos",
                "kali-linux",
                "linuxmint",
                "mageia",
                "mandriva",
                "manjaro",
                "nixos",
                "openbsd",
                "opensuse",
                "pop-os",
                "raspberry-pi",
                "redhat",
                "rocky-linux",
                "sabayon",
                "slackware",
                "snappy",
                "solus",
                "tux",
                "ubuntu",
                "void",
                "zorin"].indexOf(str)
                > -1) {
                return str;
            }
            if (['openwrt', 'linux', "immortalwrt"].indexOf(str) > -1) {
                return 'tux';
            }
            if (str == 'amazon') {
                return 'redhat';
            }
            if (str == 'arch') {
                return 'archlinux';
            }
            return '';
        },
        redirectNetwork(id) {
            // 保存当前选中的服务器ID
            this.currentServerId = id;
            this.loadedFromAPI = false;

            // 性能优化：显示加载状态，避免用户等待
            // 临时关闭 tooltip，避免渲染过程中 HTML tooltip 触发 innerHTML 报错
            this.disableTooltip();
            this.option.title.text = "Loading...";
            if (this.myChart) {
                this.myChart.setOption(this.option, true);
            }

            this.getMonitorHistory(id, this.timeRange)
                .then((monitorInfo) => {
                    this.parseMonitorInfo(monitorInfo);
                })
                .catch((error) => {
                    this.option.title.text = "Failed to load data";
                    if (this.myChart) {
                        this.myChart.setOption(this.option, true);
                    }
                    // 出错也要恢复 tooltip
                    this.enableTooltip();
                })
        },
        getMonitorHistory(id, timeRange = '24h') {
            // 性能优化：添加缓存和超时控制
            return $.ajax({
                url: "/api/v1/monitor/" + id,
                method: "GET",
                data: {
                    range: timeRange // 添加时间范围参数
                },
                timeout: 10000, // 超时时间10秒
                cache: false    // 禁用缓存以获取最新数据
            });
        },
        getMonitorName(monitorId) {
            // 恢复使用后台配置的监控名称
            if (monitorConfigs[monitorId] && monitorConfigs[monitorId].Name) {
                return monitorConfigs[monitorId].Name;
            }
            // 如果没有配置，返回默认名称
            return `Monitor ${monitorId}`;
        },
        parseMonitorInfo(monitorInfo) {
            let tSeries = [];
            let tLegendData = [];
            var lcolors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];

            // 检查数据有效性 - 支持直接数组格式和包装格式
            let dataArray = [];
            if (Array.isArray(monitorInfo)) {
                dataArray = monitorInfo;
            } else if (monitorInfo && monitorInfo.result && Array.isArray(monitorInfo.result)) {
                dataArray = monitorInfo.result;
            } else if (monitorInfo && Array.isArray(monitorInfo)) {
                dataArray = monitorInfo;
            }

            if (!dataArray || dataArray.length === 0) {
                this.option.title.text = "No Data Available";
                // 使用requestAnimationFrame避免强制重排
                requestAnimationFrame(() => {
                    if (this.myChart && typeof this.myChart.setOption === 'function') {
                        this.myChart.setOption(this.option, true);
                    }
                    // 无数据时恢复 tooltip
                    if (this.tooltipTimer) { clearTimeout(this.tooltipTimer); }
                    this.tooltipTimer = setTimeout(() => { this.enableTooltip(); }, 0);
                });
                return;
            }

            // 将原始监控历史数据转换为图表需要的格式
            const processedData = this.processMonitorHistories(dataArray);
            if (!processedData || processedData.length === 0) {
                this.option.title.text = "No Valid Data";
                requestAnimationFrame(() => {
                    if (this.myChart && typeof this.myChart.setOption === 'function') {
                        this.myChart.setOption(this.option, true);
                    }
                    // 无有效数据时也恢复 tooltip
                    if (this.tooltipTimer) { clearTimeout(this.tooltipTimer); }
                    this.tooltipTimer = setTimeout(() => { this.enableTooltip(); }, 0);
                });
                return;
            }

            // 成功解析后，认为已使用后端返回数据
            this.loadedFromAPI = true;

            // 固定图例顺序：按监测点名称（升序）排序，PC/移动端一致
            const displayData = processedData.slice().sort((a, b) => {
                const an = (a.monitor_name || '').toString();
                const bn = (b.monitor_name || '').toString();
                return an.localeCompare(bn);
            });

            // 性能优化：预计算颜色，避免重复计算
            const precomputedColors = lcolors.map(lcolor => ({
                marker: 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.5)',
                bar: 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.35)'
            }));

            for (let i = 0; i < displayData.length; i++) {
                const colors = precomputedColors[i % precomputedColors.length];
                let loss = 0;
                let data = [];
                let datal = [];

                // 性能优化：批量处理数据，减少数组访问
                const timestamps = displayData[i].created_at;
                const delays = displayData[i].avg_delay;
                const dataLength = timestamps.length;

                // 性能优化：预分配数组大小
                data = new Array(dataLength);
                datal = [];
                let dataIndex = 0;

                for (let j = 0; j < dataLength; j++) {
                    const timestamp = timestamps[j];
                    const avgDelay = Math.round(delays[j]);

                    // 性能优化：减少类型检查
                    if (avgDelay > 0 && avgDelay < MaxTCPPingValue) {
                        data[dataIndex++] = [timestamp, avgDelay];
                    } else {
                        loss++;
                        // 性能优化：只在必要时创建复杂对象
                        if (loss < 100) { // 限制丢包标记数量，避免性能问题
                            datal.push({
                                xAxis: timestamp,
                                label: { show: false },
                                emphasis: { disabled: true },
                                lineStyle: {
                                    type: "solid",
                                    color: colors.bar
                                }
                            });
                        }
                    }
                }

                // 性能优化：调整数组大小
                data.length = dataIndex;
                let lossRate = ((loss / timestamps.length) * 100).toFixed(1);
                if (lossRate > 99) {
                    datal = [];
                }
                // 直接使用后台配置的监控点名称，不添加任何前缀
                const monitorName = displayData[i].monitor_name || `Monitor ${displayData[i].monitor_id}`;

                let legendName = monitorName + " " + lossRate + "%";
                if (this.isMobile) {
                    legendName = this.fitLegendLabelToWidth(legendName);
                }
                tLegendData.push(legendName);
                // 性能优化：简化图表配置，减少渲染复杂度
                const seriesConfig = {
                    name: legendName,
                    type: 'line',
                    smooth: true,
                    symbol: 'none',
                    data: data,
                    z: 1,
                    zlevel: 0
                };

                // 性能优化：只在丢包数据不多时才添加markLine
                if (datal.length > 0 && datal.length < 50) {
                    seriesConfig.markLine = {
                        symbol: "none",
                        symbolSize: 0,
                        data: datal,
                        z: 2,
                        zlevel: 0
                    };
                }

                // 修复：放宽条件，确保极值标记能够正常显示
                if (data.length > 2) {
                    seriesConfig.markPoint = {
                        data: [
                            { type: 'max', symbol: 'pin', name: '极高值', itemStyle: { color: colors.marker }, symbolSize: 30, label: { fontSize: 8, show: true } },
                            { type: 'min', symbol: 'pin', name: '极低值', itemStyle: { color: colors.marker }, symbolSize: 30, label: { fontSize: 8, offset: [0, 7.5], show: true }, symbolRotate: 180 }
                        ],
                        z: 3,
                        zlevel: 0
                    };
                }

                tSeries.push(seriesConfig);
            }

            // 设置标题，优先使用第一个有效的服务器名称
            let titleText = "Network Monitor";
            if (displayData.length > 0 && displayData[0].server_name) {
                titleText = displayData[0].server_name;
            }

            // 批量更新配置，减少重排
            const maxLegendsPerRowMobile = parseInt(localStorage.getItem("maxLegendsPerRowMobile") || "2");
            const maxLegendsPerRowPc = parseInt(localStorage.getItem("maxLegendsPerRowPc") || "6");
            const autoIncrement = Math.floor((tLegendData.length - 1) / (this.isMobile ? maxLegendsPerRowMobile : maxLegendsPerRowPc)) * (this.isMobile ? 28 : 34);
            const height = (this.isMobile ? 440 : 480) + autoIncrement; // 减少基础高度
            const gridTop = 60 + autoIncrement;

            // 一次性更新所有配置
            this.option.title.text = titleText;
            this.option.series = tSeries;
            this.option.legend.data = tLegendData;
            // 移动端减少右侧预留，减小留白；PC 端保持原值
            this.option.legend.right = this.isMobile ? '150px' : '160px';
            this.option.grid = {
                left: this.isMobile ? '12%' : '7%', // 进一步增加左边距
                right: this.isMobile ? '12%' : '7%', // 进一步增加右边距
                top: gridTop,
                bottom: this.isMobile ? '13%' : '12%', // PC端略微增加，移动端减少
                z: 1,
                zlevel: 0
            };

            // 使用requestAnimationFrame优化渲染
            requestAnimationFrame(() => {
                if (this.myChart && typeof this.myChart.resize === 'function') {
                    this.myChart.resize({
                        width: 'auto',
                        height: height
                    });
                }
                // 不要调用 clear()，避免清除 tooltip 容器导致 innerHTML 报错
                if (this.myChart && typeof this.myChart.setOption === 'function') {
                    // 移动端也固定展示全部图例（不滚动），按钮位置微调以免遮挡
                    if (this.isMobile) {
                        try {
                            var g = this.option.graphic || [];
                            var container = this.$refs.chartDom;
                            var cw = (container && container.clientWidth) ? container.clientWidth : window.innerWidth;
                            var title = (this.option && this.option.title && this.option.title.text) ? this.option.title.text : '';
                            // 估算标题宽度
                            if (!this._measureCtx) {
                                var canvas = document.createElement('canvas');
                                this._measureCtx = canvas.getContext('2d');
                            }
                            var ctx = this._measureCtx;
                            ctx.font = '16px sans-serif';
                            var titleW = title ? ctx.measureText(title).width : 0;
                            var titleHalf = titleW / 2;
                            var titleLeft = cw / 2 - titleHalf;
                            var titleRight = cw / 2 + titleHalf;

                            var btnWidth = 60;
                            var baseTop = 12;      // 更靠近顶部
                            // 按钮向左回移一点，并使 right 与 top 一致
                            var baseRight72 = baseTop;   // 12px
                            var baseRight24 = baseRight72 + btnWidth; // 紧贴
                            // 计算 24h 按钮左边界，检测与标题水平是否重叠
                            var btn24Left = cw - baseRight24 - btnWidth;
                            var horizontalOverlap = btn24Left < (titleRight + 8);
                            var finalTop = horizontalOverlap ? 40 : baseTop;

                            g.forEach(function(it){
                                if(!it) return;
                                if(it.id==='btn72hGroup') { it.top = finalTop; it.right = baseRight72; }
                                if(it.id==='btn24hGroup') { it.top = finalTop; it.right = baseRight24; }
                            });
                        } catch(e){}
                    }
                    this.myChart.setOption(this.option, true);
                    // 重新应用时间范围按钮的选中样式，避免被整体 setOption 覆盖
                    this.updateTimeRangeButtons();
                    // 渲染完成后异步恢复 tooltip
                    if (this.tooltipTimer) { clearTimeout(this.tooltipTimer); }
                    this.tooltipTimer = setTimeout(() => { this.enableTooltip(); }, 0);
                }
            });
        },
        // 移动端：根据可用像素宽度对图例文本进行省略，尽量保持单行
        fitLegendLabelToWidth(name) {
            // 按需保留完整文本，不做省略，配合 legend.right 的左移与自适应换行
            return name;
        },
        isWindowsPlatform(str) {
            return str && typeof str === 'string' && str.includes('Windows')
        },
        renderChart() {
            const htmlTheme = $('html').attr('nz-theme');
            const chartTheme = htmlTheme === "dark" ? "dark" : "";

            // 避免重复初始化
            if (this.myChart && typeof this.myChart.isDisposed === 'function' && !this.myChart.isDisposed()) {
                this.myChart.dispose();
            }

            // 延迟初始化，确保DOM准备就绪
            requestAnimationFrame(() => {
                this.myChart = echarts.init(this.$refs.chartDom, chartTheme, {
                    renderer: 'canvas',
                    useDirtyRect: true,
                    width: 'auto',
                    height: this.isMobile ? 440 : 480 // 减少初始高度
                });

                // 恢复图表正常交互，只做必要的滚动优化
                if (this.isMobile) {
                    // 设置图表容器允许触摸操作
                    this.$refs.chartDom.style.touchAction = 'manipulation';
                }

                this.myChart.setOption(this.option, true);

                // 添加点击事件监听
                this.myChart.on('click', (params) => {
                                    if (params.componentType === 'graphic') {
                                        const idOrName = params.id || params.name || '';
                                        const ev = params.event || {};
                                        const t = (ev.target && ev.target.id) || (ev.topTarget && ev.topTarget.id) || '';
                                        const key = idOrName || t;
                                        if (key.indexOf('btn24h') !== -1) {
                                            this.switchTimeRange('24h');
                                        } else if (key.indexOf('btn72h') !== -1) {
                                            this.switchTimeRange('72h');
                                        }
                                    }
                });

                // 悬停在 graphic 按钮上时隐藏 tooltip，避免显示元素 name（例如 btn24hText）
                this.myChart.on('mouseover', (params) => {
                    if (params && params.componentType === 'graphic') {
                        const idOrName = params.id || params.name || '';
                        if (idOrName.indexOf('btn24h') !== -1) {
                            this.hoverTipLabel = '24 小时';
                        } else if (idOrName.indexOf('btn72h') !== -1) {
                            this.hoverTipLabel = '72 小时';
                        }
                        // 可选：也可以隐藏原轴向 tooltip
                        try { this.myChart.dispatchAction({ type: 'hideTip' }); } catch (e) {}
                    }
                });

                // 移出图形元素时清空自定义提示
                this.myChart.on('mouseout', (params) => {
                    if (params && params.componentType === 'graphic') {
                        this.hoverTipLabel = '';
                    }
                });

                // 鼠标离开图表区域时强制隐藏 tooltip，避免浮窗残留或叠加
                if (this.myChart && this.myChart.getZr) {
                    const zr = this.myChart.getZr();
                    if (zr && zr.on) {
                        zr.on('globalout', () => {
                            try {
                                this.myChart.dispatchAction({ type: 'hideTip' });
                            } catch (e) { /* ignore */ }
                            this.hoverTipLabel = '';
                        });
                    }
                }
            });
        },
        resizeHandle() {
            if (this.myChart && typeof this.myChart.isDisposed === 'function' && !this.myChart.isDisposed()) {
                // 使用防抖优化resize性能，增加延迟时间
                clearTimeout(this.resizeTimer);
                this.resizeTimer = setTimeout(() => {
                    // 使用requestAnimationFrame进一步优化
                    requestAnimationFrame(() => {
                        if (this.myChart && typeof this.myChart.isDisposed === 'function' && !this.myChart.isDisposed() && typeof this.myChart.resize === 'function') {
                            this.myChart.resize();
                        }
                    });
                }, 200);
            }
        },
        switchTimeRange(range) {
            // 即使与当前时间范围相同也触发刷新，确保不使用首屏的有限预取数据

            this.timeRange = range;

            // 更新按钮样式
            this.updateTimeRangeButtons();

            // 如果有选中的服务器，重新加载数据
            if (this.currentServerId) {
                // 临时关闭 tooltip，避免渲染过程中 HTML tooltip 触发 innerHTML 报错
                this.disableTooltip();
                this.option.title.text = "Loading...";
                if (this.myChart) {
                    this.myChart.setOption(this.option, true);
                }

                this.getMonitorHistory(this.currentServerId, range)
                    .then((monitorInfo) => {
                        this.parseMonitorInfo(monitorInfo);
                    })
                    .catch((error) => {
                        this.option.title.text = "Failed to load data";
                        if (this.myChart) {
                            this.myChart.setOption(this.option, true);
                        }
                        // 出错也要恢复 tooltip
                        if (this.tooltipTimer) { clearTimeout(this.tooltipTimer); }
                        this.tooltipTimer = setTimeout(() => { this.enableTooltip(); }, 0);
                    });
            } else {
                // 如果没有选中服务器，使用初始数据但根据时间范围过滤
                this.disableTooltip();
                this.filterInitialDataByTimeRange(range);
            }
        },
        // 规避 HTML tooltip 在 DOM 不稳定时的 innerHTML 报错
        disableTooltip() {
            if (!this.myChart) return;
            try { this.myChart.dispatchAction({ type: 'hideTip' }); } catch (e) {}
            if (this.tooltipTimer) { clearTimeout(this.tooltipTimer); this.tooltipTimer = null; }
            try { this.myChart.setOption({ tooltip: { show: false, showContent: false, triggerOn: 'none', appendToBody: false } }, false); } catch (e) {}
        },
        enableTooltip() {
            if (!this.myChart) return;
            try { this.myChart.setOption({ tooltip: { show: true, showContent: true, triggerOn: 'mousemove', appendToBody: false } }, false); } catch (e) {}
        },
        updateTimeRangeButtons() {
            if (!this.myChart) return;

            // 更新按钮样式，简洁透明风格
            // 1) 同步到 this.option.graphic，防止后续整体 setOption 覆盖
            if (Array.isArray(this.option.graphic)) {
                const applyToGroup = (groupId, rectId, textId, active) => {
                    const group = this.option.graphic.find(g => g && g.id === groupId);
                    if (group && Array.isArray(group.children)) {
                        const rect = group.children.find(c => c && c.id === rectId);
                        const text = group.children.find(c => c && c.id === textId);
                        if (rect) {
                            rect.style = rect.style || {};
                            rect.style.fill = active ? 'rgba(255,255,255,0.6)' : 'rgba(255,255,255,0.4)';
                        }
                        if (text) {
                            text.style = text.style || {};
                            text.style.fontWeight = active ? 'bold' : 'normal';
                            text.style.fill = active ? '#333' : '#666';
                        }
                    }
                };
                applyToGroup('btn24hGroup', 'btn24hRect', 'btn24hText', this.timeRange === '24h');
                applyToGroup('btn72hGroup', 'btn72hRect', 'btn72hText', this.timeRange === '72h');
            }

            // 2) 立即更新图表上的样式
            this.myChart.setOption({
                graphic: [
                    // 24h 背景
                    { id: 'btn24hRect', style: { fill: this.timeRange === '24h' ? 'rgba(255,255,255,0.6)' : 'rgba(255,255,255,0.4)' } },
                    // 24h 文字
                    { id: 'btn24hText', style: { fontWeight: this.timeRange === '24h' ? 'bold' : 'normal', fill: this.timeRange === '24h' ? '#333' : '#666' } },
                    // 72h 背景
                    { id: 'btn72hRect', style: { fill: this.timeRange === '72h' ? 'rgba(255,255,255,0.6)' : 'rgba(255,255,255,0.4)' } },
                    // 72h 文字
                    { id: 'btn72hText', style: { fontWeight: this.timeRange === '72h' ? 'bold' : 'normal', fill: this.timeRange === '72h' ? '#333' : '#666' } }
                ]
            });
        },
        filterInitialDataByTimeRange(range) {
            // 根据时间范围过滤初始数据
            this.loadedFromAPI = false; // 预取数据不算后端完整数据
            const now = Date.now();
            const timeLimit = range === '24h' ? 24 * 60 * 60 * 1000 : 72 * 60 * 60 * 1000; // 改为72小时
            const cutoffTime = now - timeLimit;

            // 过滤 monitorInfo 数据
            const filteredData = monitorInfo.filter(item => {
                const itemTime = Date.parse(item.CreatedAt || item.created_at);
                return itemTime >= cutoffTime;
            });

            this.parseMonitorInfo(filteredData);
        },
    },
    beforeDestroy() {
        // 清理定时器
        if (this.resizeTimer) {
            clearTimeout(this.resizeTimer);
        }
        // 清理图表实例
        if (this.myChart && typeof this.myChart.isDisposed === 'function' && !this.myChart.isDisposed()) {
            this.myChart.dispose();
        }
        this.myChart = null;
    },
        });
    });
</script>

{{end}}